function gewGrassNode(gl,name,heightMap)
{ 
  gewNode.call(this,name);
  this.gl = gl;
  // Create gl buffer objects
  this.glBuffers = {};
  var glBuffers = this.glBuffers;
  glBuffers.positions = gl.createBuffer();
  glBuffers.texcoords= gl.createBuffer();
  glBuffers.indices = gl.createBuffer();
  glBuffers.offsets = gl.createBuffer();
  this.vertices = [];
  this.texcoords = [];
  this.offsets  = [];
  this.indices = [];
  this.indicesNum  = 0;
  this.clumpNum = 0;
  this.clumpRatioRange = new gewVec2(1.8,2.2);
  this.clumpHeightRange = new gewVec2(0.3,1);
  this.type0Percent = 20;
  this.type1Percent = 20;
  this.type2Percent = 30;
  this.type3Percent = 30;
  this.heightMap = heightMap;
  // PRIVATE
  this.isCalcModelMat = false;
  this.isNeedPlantByHeightMap = false; //  
  // these properties only use by level editor
  this.keepBrushs = false; // only use for level design to keep brush infos to save again
  this.boxBrushs = [];
  this.pointBrushs = [];
  this.clumpPositions = [];
}
extend2(gewGrassNode, gewNode);
gewGrassNode.prototype.plantByHeightMap = function()
{
  var pointBrushs = this.pointBrushs;
  var boxBrushs = this.boxBrushs;
  var brushNum = pointBrushs.length;
  var brushData = pointBrushs;
  delete this.pointBrushs;
  delete this.boxBrushs;
  if (this.keepBrushs) {
    this.boxBrushs = [];
    this.pointBrushs = [];
  }
  for(var i=0;i<brushNum;i++)
  {
    var box = brushData[i];
    this.addGrassClump(box[0],box[1],box[2]);
    if (this.keepBrushs) {
      this.pointBrushs.push( new gewVec3(box[0],box[1],box[2] ) );      
    }
  }      
  brushNum = boxBrushs.length;
  brushData = boxBrushs;
  for(var i=0;i<brushNum;i++)
  {
    var box = brushData[i];
    this.addGrassClumpByBoxBrush(box[0],box[1],box[2],box[3],box[4]);
    
    if (this.keepBrushs) {
      var brush = new gewRec2D(box[0],box[1],box[2],box[3]);
      brush.density = box[4];
      this.boxBrushs.push( brush );      
    }
  }      
  this.updateGLBuffers();
  this.isNeedPlantByHeightMap = false;
  this.updateWorldAabb();
  this.updateWorldSphere();
}
gewGrassNode.prototype.getGrassType = function()
{
  var r = Math.random()*100;
  var type = 0;
  if( r < this.type0Percent)
    type = 0;
  else if( r < (this.type0Percent+this.type1Percent) )
    type = 1;
  else if( r < (this.type0Percent+this.type1Percent+this.type2Percent ) )
    type = 2;
  else if( r < (this.type0Percent+this.type1Percent+this.type2Percent+this.type3Percent ) )
    type = 3;
  else // it should not run into here
    type = 0;
  return type;
}
gewGrassNode.prototype.addGrassClumpByBoxBrush = function(minx,maxx,miny,maxy,density)
{
  var heightMap = this.heightMap;
  var gridSize = 1/Math.sqrt(density);
  for(var dx=minx;dx<maxx;dx+=gridSize)
  for(var dz=miny;dz<maxy;dz+=gridSize)
  {      
    var maxdx = dx+gridSize;
    var maxdz = dz+gridSize;
    var x = gewMix(dx, maxdx, Math.random());
    var z = gewMix(dz, maxdz, Math.random());
    if(heightMap)
      var y = heightMap.interpolationAt(x,z);
    else
      var y = 0;
    if(this.clumpNum>10800)        
      break;
    this.addGrassClump(x,y,z);
  }  
}
gewGrassNode.prototype.addGrassClump = function(x,y,z) 
{
  if(this.clumpNum>10800)
  {
    alert("gewGrassNode::addGrassClump: Can't not add more clump for this Grass node( maximum 10800 reach)");
    return;
  }
  //this.clumpPositions.push(new gewVec3(x,y,z));
  this.clumpNum++;    
  // Choice grass type for this clump;
  var type = this.getGrassType();
  var ver = this.vertices;
  var tex = this.texcoords;
  var off = this.offsets;
  var ind = this.indices;
  var r = Math.random();
  var ratio = this.clumpRatioRange.x*r + this.clumpRatioRange.y*(1-r);
  r = Math.random();
  var height = this.clumpHeightRange.x*r+this.clumpHeightRange.y*(1-r); 
  var hw = (ratio*height)/2;
  
  var vec = new gewVec3(x,y,z);
  if(!this.localAabb)
  {
    this.localAabb = new gewAabb(vec,vec);
  }
  else
  {
    this.localAabb.addInternalPoint(vec);
  }
  var center = this.localAabb.max.add(this.localAabb.min).divScalar(2);
  this.localSphere = new gewSphere(center,0);
  this.localSphere.addInternalPoint(this.localAabb.max);
  this.localSphere.addInternalPoint(this.localAabb.min);
  this.setPositionXYZ(0,0,0);
  var offset = ver.length/3;
  ver.push(x,y,z,
           x,y,z,
           x,y,z,
           x,y,z);
  var minU,maxU;
  switch(type)
  {
    case 0:
      minU = 0;maxU=0.25;
      break;
    case 1:
      minU = 0.25;maxU=0.5;
      break;
    case 2:
      minU = 0.5;maxU=0.75;
      break;
    case 3:
      minU = 0.75;maxU=1.;
      break;      
  }
  r = Math.random();
  if( r>0.5) // 50% swap u
  {
    var tempU = minU;
    minU = maxU;
    maxU = tempU;
  }
  tex.push(minU,0,
           minU,1,
           maxU,1,
           maxU,0);
  off.push(-hw,0.0,
           -hw,height,
           hw,height,
           hw,0.0);
  ind.push(offset,offset+1,offset+2,
           offset,offset+2,offset+3);
}
gewGrassNode.prototype.updateGLBuffers = function()
{
  var gl = this.gl;
  var ver = this.vertices;
  var tex = this.texcoords;
  var ind = this.indices;
  var off = this.offsets;
  this.indicesNum = ind.length;
  var glBuffers = this.glBuffers 
  gl.bindBuffer(gl.ARRAY_BUFFER, glBuffers.positions);
  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(ver), gl.STATIC_DRAW);
   
  gl.bindBuffer(gl.ARRAY_BUFFER, glBuffers.texcoords);
  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(tex), gl.STATIC_DRAW); 
  
  gl.bindBuffer(gl.ARRAY_BUFFER, glBuffers.offsets);
  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(off), gl.STATIC_DRAW); 
  
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, glBuffers.indices);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray(ind), gl.STATIC_DRAW);
}
gewGrassNode.prototype.render = function(material)
{  
  if(this.clumpNum==0)//no data
    return;
  var gl = this.gl;
  var effect = material.effect;
  //Set up vertex att pointers
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.positions);
  gl.vertexAttribPointer(effect.inPosition, 3, gl.FLOAT, false, 0, 0);
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.texcoords);
  gl.vertexAttribPointer(effect.inTexCoord0, 2, gl.FLOAT, false, 0, 0);
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.offsets);
  gl.vertexAttribPointer(effect.inOffset, 2, gl.FLOAT, false, 0, 0);
  
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.glBuffers.indices);
  gl.drawElements(gl.TRIANGLES,this.indicesNum,gl.UNSIGNED_SHORT,0);    
}

